import { DependencyList, useEffect, useMemo, useState, } from "react";

export type ExtractResult<T> = T extends () => Promise<infer R> ? R : any;
export interface IUseAsyncState<T> {
    pending: boolean;
    result: null | ExtractResult<T>;
    error: any;
}
export function useAsyncFun<T extends (...arg: any[]) => Promise<any>>(func: T, deps: DependencyList) {
    const [pending, setPending] = useState(false);
    const [result, setResult] = useState<ExtractResult<T | null>>(null);

    const wrappedFunc = useMemo(() => {
        return function () {
            setPending(true);
            const promise = func.apply(null, arguments as any);
            promise.then(res => setResult(res));
            promise.finally(() => setPending(false));
            return promise;
        }
    }, deps);
    return [wrappedFunc, pending, result] as const;
}

export function useAsync<T extends (...arg: []) => Promise<any>>(func: T, deps: DependencyList) {
    const [state, setState] = useState<IUseAsyncState<T>>({
        pending: true,
        result: null,
        error: null
    })

    useEffect(() => {
        func().then(res => {
            setState({
                pending: false,
                result: res,
                error: null
            })
        }).catch(err => {
            setState({
                pending: false,
                result: null,
                error: err
            })
        })
    }, deps)

    return state;
}
